package folder.model.virtual;

import java.util.ArrayList;
import java.util.List;

public class VirtualNetworkRandom
{	
	private static final double BANDWIDTH_MIN = 1;
	private static final double BANDWIDTH_MAX = 1;
	
	private static final int NUMBER_VIRTUAL_NODES_MIN = 3; // Minimum 3
	private static final int NUMBER_VIRTUAL_NODES_MAX = 3;
	
	private int numberVirtualNodes;
	private List< VirtualNode > virtualNodes;
	
	private int numberVirtualLinks;
	private List< VirtualLink > virtualLinks;
	
	private VirtualNetwork virtualNetwork;
	
	public VirtualNetworkRandom( int identifier )
	{
		this.virtualNetwork = VirtualNetwork.virtualNetwork( identifier );
		
		this.setNumberVirtualNodes();
		this.setVirtualNodes();
		this.setNumberVirtualLinks();
		this.setVirtualLinks();
		this.setVirtualNetwork();
	}
	
	private void setNumberVirtualNodes()
	{
		this.numberVirtualNodes = (int) this.randomMinMax( NUMBER_VIRTUAL_NODES_MIN, NUMBER_VIRTUAL_NODES_MAX );
	}
	
	private void setVirtualNodes()
	{
		this.virtualNodes = new ArrayList< VirtualNode >();
		
		for( int i = 0; i < this.numberVirtualNodes; i++ )
		{
			VirtualNode virtualNode = VirtualNode.virtualNode( i, this.virtualNetwork );
			this.virtualNodes.add( virtualNode );
		}
	}
	
	private void setNumberVirtualLinks()
	{
		int numberVirtualLinksMin = this.numberVirtualNodes;
		int numberVirtualLinksMax = ( this.numberVirtualNodes * ( this.numberVirtualNodes - 1 ) ) / 2;
		this.numberVirtualLinks = (int) this.randomMinMax( numberVirtualLinksMin, numberVirtualLinksMax );
	}
	//
	private void setVirtualLinks()
	{
		this.createLine();
		
		if(this.numberVirtualLinks == 2)
			return;
		
		this.createRing();
		this.completeLinks();
	}
	
	private void createLine()
	{
		this.virtualLinks = new ArrayList< VirtualLink >();
		
		for( int i = 0; i < this.numberVirtualNodes - 1; i++ )
		{
			VirtualNode virtualNodeA = this.virtualNodes.get( i );
			VirtualNode virtualNodeB = this.virtualNodes.get( i + 1 );
			VirtualLink virtualLinkRandom = this.virtualLinkRandom( i , virtualNodeA, virtualNodeB );
			this.virtualLinks.add( virtualLinkRandom );
		}
	}
	
	private void createRing()
	{
		int identifier = this.numberVirtualNodes - 1;
		VirtualNode virtualNodeA = this.virtualNodes.get( identifier );
		VirtualNode virtualNodeB = this.virtualNodes.get( 0 );
		VirtualLink virtualLinkRandom = this.virtualLinkRandom( identifier, virtualNodeA, virtualNodeB );
		this.virtualLinks.add( virtualLinkRandom );
	}
	
	private void completeLinks()
	{
		int identifier = this.virtualLinks.size();
		
		while( identifier < this.numberVirtualLinks )
		{
			int min = 0;
			int max = this.numberVirtualNodes - 1;
			int identifierRandom;
			VirtualNode virtualNodeA;
			VirtualNode virtualNodeB;
			boolean checkVirtualNodes;
			
			do{
				identifierRandom = (int) this.randomMinMax( min, max );
				virtualNodeA = this.virtualNodes.get( identifierRandom );
				
				identifierRandom = (int) this.randomMinMax( min, max );
				virtualNodeB = this.virtualNodes.get( identifierRandom );
				
				checkVirtualNodes = this.checkVirtualNodes( virtualNodeA, virtualNodeB );
				
			}while( !checkVirtualNodes );
			
			VirtualLink virtualLink = this.virtualLinkRandom( identifier++ , virtualNodeA, virtualNodeB );
			this.virtualLinks.add( virtualLink );
		}
	}
	
	private boolean checkVirtualNodes( VirtualNode virtualNodeA, VirtualNode virtualNodeB )
	{
		if( virtualNodeA.equals( virtualNodeB ) )
			return false;
		else
			for( VirtualLink virtualLink: this.virtualLinks )
				if( ( virtualLink.getVirtualNodeA().equals( virtualNodeA ) && virtualLink.getVirtualNodeB().equals( virtualNodeB ) ) || 
					( virtualLink.getVirtualNodeA().equals( virtualNodeB ) && virtualLink.getVirtualNodeB().equals( virtualNodeA ) ) )
					return false;
		
		return true;
	}
	
	private VirtualLink virtualLinkRandom( int identifier, VirtualNode virtualNodeA, VirtualNode virtualNodeB )
	{	
		double bandwidth = this.randomMinMax( BANDWIDTH_MIN, BANDWIDTH_MAX );
		VirtualLink virtualLink = VirtualLink.virtualLink( identifier, bandwidth, virtualNodeA, virtualNodeB, this.virtualNetwork );
		
		return  virtualLink;
	}
	
	private double randomMinMax( double min, double max )
	{
		double random = min + (int) ( Math.random() * ( max - min + 1 ) );
		
		return random;
	}

	private void setVirtualNetwork()
	{
		this.virtualNetwork.setVirtualNodes( this.virtualNodes );
		this.virtualNetwork.setVirtualLinks( this.virtualLinks );
	}
	
	public static VirtualNetwork virtualNetworkRandom( int identifier )
	{
		VirtualNetworkRandom virtualNetworkRandom = new VirtualNetworkRandom( identifier );
		VirtualNetwork virtualNetwork = virtualNetworkRandom.virtualNetwork;
		
		return virtualNetwork;
	}

}
